//
//  Application.swift
//  UICore
//
//  Created by 蒋晨成 on 2021/12/23.
//

import Chain
import Foundation
import Service
import UIKit

public enum Application {}

extension Application: TypeName {}

public extension Application {
    static let logger = Loggers[typeName]
}

private extension Application {
    static let lock = ReadWriteLock()
    static var observers = [AnyApplicationObservable]()
}

public extension Application {
    private(set) static var windowScenes = [UIWindowScene]()
    private(set) static var connectionOptions: UIScene.ConnectionOptions?

    static let window: UIWindow = {
        guard let mainWindowScene = windowScenes.first else { fatalError("Main WindowScene Not Found!") }
        return UIWindow(windowScene: mainWindowScene)
            .x
            .backgroundColor(.white)
            .makeKeyAndVisible()
            .instance
    }()

    static func append(windowScene: UIWindowScene) {
        windowScenes.append(windowScene)
    }
}

public extension Application {
    static func cache(connectionOptions: UIScene.ConnectionOptions) {
        self.connectionOptions = connectionOptions
    }

    static func hasHandledConnectionOptions() {
        connectionOptions = nil
    }
}

public extension Application {
    static func add(observable: ApplicationObservable) {
        lock.withWriterLockVoid { observers.append(AnyApplicationObservable(observable: observable)) }
    }
}

private extension Application {
    static func flatten() {
        lock.withWriterLockVoid { observers = observers.filter { !$0.isRemovable } }
    }
}

public extension Application {
    static func application(_: UIApplication, willFinishLaunchingWithOptions _: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        true
    }
}

public extension Application {
    static func applicationWillTerminate(_: UIApplication) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.applicationWillTerminate() } }
    }
}

public extension Application {
    static func sceneDidDisconnect(_ scene: UIScene) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.sceneDidDisconnect(scene) } }
    }

    static func sceneDidBecomeActive(_ scene: UIScene) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.sceneDidBecomeActive(scene) } }
    }

    static func sceneWillResignActive(_ scene: UIScene) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.sceneWillResignActive(scene) } }
    }

    static func sceneWillEnterForeground(_ scene: UIScene) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.sceneWillEnterForeground(scene) } }
    }

    static func sceneDidEnterBackground(_ scene: UIScene) {
        flatten()
        lock.withReaderLockVoid { observers.forEach { $0.observable?.sceneDidEnterBackground(scene) } }
    }
}

public extension Application {
    static func applicationDidReceiveMemoryWarning(_: UIApplication) {}
}

public extension Application {
    static func application(_: UIApplication, continue _: NSUserActivity, restorationHandler _: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool {
        false
    }

    static func canOpen(url: URL) -> Bool {
        UIApplication.shared.canOpenURL(url)
    }
}

public extension Application {
    static func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
        flatten()
        lock.withReaderLock {
            observers.forEach { $0.observable?.scene(scene, openURLContexts: URLContexts) }
        }
    }

    static func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        flatten()
        lock.withReaderLock {
            observers.forEach { $0.observable?.scene(scene, continue: userActivity) }
        }
    }
}

public extension Application {
    static func application(_: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
        Bootstrapper.internalEnvironment.application(didRegisterForRemoteNotificationsWithDeviceToken: deviceToken)
    }

    static func application(_: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
        Bootstrapper.internalEnvironment.application(didFailToRegisterForRemoteNotificationsWithError: error)
    }

    static func application(
        _: UIApplication,
        didReceiveRemoteNotification userInfo: [AnyHashable: Any],
        fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void
    ) {
        Bootstrapper.internalEnvironment.application(didReceiveRemoteNotification: userInfo, fetchCompletionHandler: completionHandler)
    }
}

public extension Application {
    static func beginBackgroundTask(name: String? = nil, expirationHandler: (() -> Void)? = nil) -> UIBackgroundTaskIdentifier {
        UIApplication.shared.beginBackgroundTask(withName: name, expirationHandler: expirationHandler)
    }

    static func endBackgroundTask(identifier: UIBackgroundTaskIdentifier) {
        UIApplication.shared.endBackgroundTask(identifier)
    }
}

public extension Application {
    static func idleTimerEnable() {
        UIApplication.shared.isIdleTimerDisabled = false
    }

    static func idleTimerDisable() {
        UIApplication.shared.isIdleTimerDisabled = true
    }
}

public extension Application {
    static func registerRemoteNotification() {
        UIApplication.shared.registerForRemoteNotifications()
    }
}
